/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.tencent.tendon.convert.json.streams;

import com.tencent.tendon.convert.util.*;
import com.tencent.tendon.convert.json.*;

/**
 *
 * @author nbzhang
 */
public abstract class JsonAbstractReader extends JsonReader {

    private char lastedchar;

    protected abstract char get();

    protected final CharArray buf = JsonPool.pollCharArray();

    @Override
    public void release() {
        super.release();
        JsonPool.offer(buf);
    }

    @Override
    protected char nextChar() {
        this.position++;
        if (lastedchar != 0) {
            char ch = lastedchar;
            lastedchar = 0;
            return ch;
        }
        return get();
    }

    @Override
    protected void back(char ch) {
        this.position--;
        this.lastedchar = ch;
    }

    @Override
    public final JsonDeHandle readField(final JsonDeHandle handle) {
        final char[] fields = handle.fields;
        final char quote = readChar();
        final int size = fields.length;
        final CharArray array = this.buf;
        array.clear();
        int i = 0;
        if (quote == '"' || quote == '\'') {
            for (;;) {
                char ch = nextChar();
                if (ch == quote || ch < 0) break;
                if (i >= size || ch != fields[i]) i = size;
                array.add(ch);
                i++;
            }
            if (i == size) return handle;
        } else {
            array.add(quote);
            i++;
            for (;;) {
                char ch = nextChar();
                if (ch == ':' || ch == '}' || ch == ']' || ch <= ' ' || ch == ',') {
                    back(ch);
                    break;
                }
                if (i >= size || ch != fields[i]) i = size;
                array.add(ch);
                i++;
            }
            if (i == size) return handle;
        }
        char[] fs = array.toArray();
        JsonDeHandle result = handle.find(fs, 0, fs.length);
        if (result == null && fs.length == 1 && fs[0] == '@') return JsonDeHandle.REFER;
        return result;
    }

    @Override
    public final char[] readValue() {
        char ch = readChar();
        if (ch < 0) return null;
        if (ch == '"' || ch == '\'') return readValue(ch);
        final CharArray array = this.buf;
        array.clear();
        array.add(ch);
        for (;;) {
            ch = nextChar();
            if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') {
                back(ch);
                break;
            }
            array.add(ch);
        }
        char[] rs = array.toArray();
        if (rs.length == 4 && rs[0] == 'n' && rs[1] == 'u' && rs[2] == 'l' && rs[3] == 'l') return null;
        return rs;
    }

    @Override
    public final int readInt() {
        final char[] text = readValue();
        if (text == null || text.length == 0) return 0;
        int value = 0;
        final boolean negative = text[0] == '-';
        char ch;
        for (int i = negative ? 1 : 0, size = text.length; i < size; i++) {
            ch = text[i];
            if (ch < '0' || ch > '9') throw new NumberFormatException("illegal escape(" + ch + ") (position = " + position + ")");
            value = (value << 3) + (value << 1) + (ch - '0');
        }
        return negative ? -value : value;
    }

    @Override
    public final long readLong() {
        final char[] text = readValue();
        if (text == null || text.length == 0) return 0L;
        long value = 0;
        final boolean negative = text[0] == '-';
        char ch;
        for (int i = negative ? 1 : 0, size = text.length; i < size; i++) {
            ch = text[i];
            if (ch < '0' || ch > '9') throw new NumberFormatException("illegal escape(" + ch + ") (position = " + position + ")");
            value = (value << 3) + (value << 1) + (ch - '0');
        }
        return negative ? -value : value;
    }

    @Override
    public final String readString() {
        char[] rs = readValue();
        return rs == null ? null : new String(rs);
    }

    private char[] readValue(final char expected) {
        final CharArray array = this.buf;
        array.clear();
        for (;;) {
            char ch = nextChar();
            if (ch == '\\') {
                ch = nextChar();
                switch (ch) {
                    case '"':
                    case '\'':
                    case '\\':
                    case '/':
                        array.add(ch);
                        break;
                    case 'n':
                        array.add('\n');
                        break;
                    case 'r':
                        array.add('\r');
                        break;
                    case 'u':
                        array.add((char) Integer.parseInt(new String(new char[]{nextChar(), nextChar(), nextChar(), nextChar()}), 16));
                        break;
                    case 't':
                        array.add('\t');
                        break;
                    case 'b':
                        array.add('\b');
                        break;
                    case 'f':
                        array.add('\f');
                        break;
                    default:
                        throw new JsonException("illegal escape(" + ch + ") (position = " + this.position + ")");
                }
            } else if (ch == expected || ch < 0) {
                break;
            } else {
                array.add(ch);
            }
        }
        return array.toArray();
    }
}
